﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;

using Outcoder.Collections;
using Outcoder.UI.Xaml;
using Outcoder.UI.Xaml.Resources;

using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(AppBar), typeof(Outcoder.UI.Xaml.Renderers.AppBarRenderer))]

namespace Outcoder.UI.Xaml.Renderers
{
	public class AppBarRenderer : ViewRenderer<AppBar, Android.Views.View>
	{
		string menuToolbarItemText = " ... "; //˅ ☰

		//static bool pageHasToolbar;
		bool initialized;
		static WeakReference<Page> toolbarOwner;
		IImageUrlTransformer imageUrlTransformer;

		protected override void OnElementChanged(ElementChangedEventArgs<AppBar> e)
		{
			base.OnElementChanged(e);

			var oldModel = e.OldElement;

			if (oldModel != null)
			{
				DetachAppBar(oldModel);
			}

			if (!initialized)
			{
				initialized = true;

				imageUrlTransformer = Dependency.Resolve<IImageUrlTransformer, ImageUrlTransformer>(true);

				Page page = GetHostContentPage();
				page.Disappearing += delegate
				{
					Page ownerPage;
					if (toolbarOwner != null && toolbarOwner.TryGetTarget(out ownerPage))
					{
						var hostContentPage = GetHostContentPage();
						if (ownerPage != hostContentPage)
						{
							return;
						}
					}

					RemoveToolbar();
				};

				page.Appearing += delegate
				{
					Page ownerPage = GetHostContentPage();
					toolbarOwner = new WeakReference<Page>(ownerPage);
					/* This next call is invoked to delay until the BindingContext is properly set. */
					Invoke(UpdateAppBar);
				};

				var nativeControl = new Android.Views.View(Context);

				SetNativeControl(nativeControl);
			}

			AppBar newAppBar = e.NewElement;
			AttachAppBar(newAppBar);
		}

		void Invoke(Action action)
		{
			UISynchronizationContext.Instance.InvokeWithoutBlocking(action);
		}

		void RemoveToolbar()
		{
			var page = GetHostNavigationPage();
			page.ToolbarItems.Clear();
		}

		void AttachAppBar(AppBar appBar)
		{
			if (appBar == null)
			{
				return;
			}

			DetachAppBar(appBar);

			appBar.ButtonCollectionChanged += HandleButtonsChanged;
			appBar.MenuItemCollectionChanged += HandleMenuItemsChanged;
		}

		void DetachAppBar(AppBar appBar)
		{
			if (appBar == null)
			{
				return;
			}

			appBar.ButtonCollectionChanged -= HandleButtonsChanged;
			appBar.MenuItemCollectionChanged -= HandleMenuItemsChanged;
		}

		void HandleMenuItemsChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			UpdateAppBar();
		}

		void HandleButtonsChanged(object sender, NotifyCollectionChangedEventArgs e)
		{
			UpdateAppBar();
		}

		NavigationPage GetHostNavigationPage()
		{
			return GetParentOfType<NavigationPage>();
		}

		ContentPage GetHostContentPage()
		{
			return GetParentOfType<ContentPage>();
		}

		T GetParentOfType<T>() where T : class
		{
			T page = null;
			Element parent = Element;
			while (page == null)
			{
				parent = parent.Parent;
				if (parent == null)
				{
					break;
				}
				page = parent as T;
			}

			return page;
		}

		void UpdateAppBar()
		{
			var appBar = Element;

			Page page = GetHostContentPage();

			if (page == null)
			{
				return;
			}

			page.ToolbarItems.Clear();

			List<ToolbarItem> items = null;

			foreach (IAppBarItem appBarItem in appBar.Buttons.ToList())
			{		
				ToolbarItem item = new ToolbarItem();
				string text;

				if (!AppBarItemPropertyResolver.TryGetItemText(appBarItem, out text))
				{
					throw new Exception("Unable to resolve text for button.");
				}

				item.Text = text;

				IAppBarItem itemForClosure = appBarItem;
				item.Clicked += (sender, e) => itemForClosure.PerformTap();

				string iconUrl;

				if (AppBarItemPropertyResolver.TryGetItemUrl(appBarItem, out iconUrl))
				{
					string transformedUrl = imageUrlTransformer.TransformForCurrentPlatform(iconUrl);
					item.Icon = transformedUrl;
				}
				
				if (items == null)
				{
					items = new List<ToolbarItem>();
				}

				items.Add(item);
			}

			var menuItemsEnumerable = appBar.MenuItems;
			if (menuItemsEnumerable != null)
			{
				var menuItems = menuItemsEnumerable.ToList();
				int menuItemCount = menuItems.Count;
				if (menuItemCount > 0)
				{
					string[] buttonTitles = new string[menuItemCount];
					for (int i = 0; i < menuItemCount; i++)
					{
						string text;

						if (!AppBarItemPropertyResolver.TryGetItemText(menuItems[i], out text))
						{
							throw new Exception("Unable to resolve text for menu item.");
						}
						buttonTitles[i] = text;
					}

					ToolbarItem item = new ToolbarItem {Text = menuToolbarItemText};
					item.Clicked += async (sender, e) =>
					{
						var buttonIndex = await DisplayMenu(buttonTitles);
						if (buttonIndex > -1)
						{
							var selectedItem = menuItems[buttonIndex];
							selectedItem.PerformTap();
						}
					};

					if (items == null)
					{
						items = new List<ToolbarItem>();
					}

					items.Add(item);
				}
			}

			if (items != null && items.Any())
			{
				page.ToolbarItems.AddRange(items);
			}
		}



		Task<int> DisplayMenu(params string[] buttons)
		{
			ActionDialogArguments arguments = new ActionDialogArguments(null, null, null, buttons);
			ActionDialog dialog = new ActionDialog(arguments, Context);

			dialog.Show();

			return arguments.Result.Task;
		}

	}
}
